package org.domain.tree;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.domain.tree.domain.Node;
import org.richfaces.model.TreeNode;


/**
 * Class for representation of tree model.
 * 
 * @author aareshchanka
 */

public class FormTreeNode implements TreeNode {

	private static final long serialVersionUID = -5498990493803705085L;
	private int formIndex;
	private int totalInstances = -1;
	private Object data;
	private TreeNode parent;
	private List<Map.Entry> childrenList = new LinkedList<Map.Entry>();
	private Node node;
	
	/** 
	 * minimal constructor
	 * 
	 * @param data {@link Object} instance to populate data field
	 * */
	public FormTreeNode(Object data) {
		super();
		this.data = data;
	}

	/** 
	 * default constructor
	 * */
	public FormTreeNode() {
		super();
	}

	/** 
	 * public constructor
	 * 
	 * @param node {@link Node} instance to populate node field 
	 * @param currentRootNode {@link FormTreeNode} instance to define parent field
	 * */
	public FormTreeNode(Node node, FormTreeNode currentRootNode) {
		super();
		this.node = node;
		this.parent = (TreeNode) currentRootNode;
	}
	
	/** 
	 * adds child node
	 * 
	 * @param childNode instance of child node to be added
	 * */
	public void addChildNode(FormTreeNode childNode) {
		addChild(childNode.getKey(), childNode);
	}

	/**
	 * @return the formIndex
	 */
	public int getFormIndex() {
		return formIndex;
	}

	/**
	 * @return the totalInstances
	 */
	public int getTotalInstances() {
		return totalInstances;
	}

	/**
	 * @param totalInstances the totalInstances to set
	 */
	public void setTotalInstances(int totalInstances) {
		this.totalInstances = totalInstances;
	}

	/**
	 * @return the data
	 */
	public Object getData() {
		return this;
	}

	/** 
	 * returns child instance by id
	 * 
	 * @param identifier id of child
	 * @return {@link TreeNode} instance of child, can be casted to {@link FormTreeNode}
	 * */
	public TreeNode getChild(Object identifier) {
		for (Map.Entry e : childrenList) {
			if (e.getKey().equals(identifier)) {
				return (FormTreeNode) e.getValue();
			}
		}
		return null;
	}

	/** 
	 * adds child into current node
	 * 
	 * @param  identifier id of child to be added
	 * @param child instance of child to be added
	 * */
	public void addChild(Object identifier, TreeNode child) {
		if (!(child instanceof FormTreeNode)) {
			throw new IllegalArgumentException(
					"child should be instanceof FormTreeNode");
		}
		child.setParent(this);
		childrenList.add(new EntryImpl(identifier, child));
	}

	/** 
	 * removes child from current node
	 * 
	 * @param identifier id of child to be removed
	 * */
	public void removeChild(Object identifier) {
		for (int i = 0; i < childrenList.size(); i++) {
			FormTreeNode f = (FormTreeNode) childrenList.get(i).getValue();
			if (f == null) {
				continue;
			}
			if (childrenList.get(i).getKey().equals(identifier)) {
				childrenList.remove(i);
			}
		}
	}

	/**
	 * @param data the data to set
	 */
	public void setData(Object data) {
		this.data = data;
	}

	/**
	 * @return the parent
	 */
	public TreeNode getParent() {
		return parent;
	}

	/**
	 * @param parent the parent to set
	 */
	public void setParent(TreeNode parent) {
		this.parent = parent;
	}

	/** 
	 * @return {@link Iterator} of current childrenList
	 * */
	public Iterator getChildren() {
		return childrenList.iterator();
	}

	/** 
	 * return true if current Node is leaf, otherwise false
	 * */
	public boolean isLeaf() {
		return childrenList.isEmpty();
	}

	/** 
	 * @return key for current node  
	 * */
	public String getKey() {
		if (getNode() == null) {
			return "_" + getFormIndex();
		}
		return getNode().getId() + "_" + getFormIndex();
	}

	/** 
	 * inner class for iteration through children list
	 * */
	public class EntryImpl implements Map.Entry {
		private Object key = null;
		private Object value = null;

		/** 
		 * public full constructor
		 * 
		 * @param key key value
		 * @param value 
		 * */
		public EntryImpl(Object key, Object value) {
			this.key = key;
			this.value = value;
		}

		/**
		 * @return the key
		 */
		public Object getKey() {
			return key;
		}

		/**
		 * @return the value
		 */
		public Object getValue() {
			return value;
		}

		/**
		 * @param value the value to set
		 */
		public Object setValue(Object value) {
			Object tmpV = value;
			this.value = value;
			return tmpV;
		}
	}

	/**
	 * @return the childrenList
	 */
	public List<Map.Entry> getChildrenList() {
		return childrenList;
	}

	/**
	 * @return the node
	 */
	public Node getNode() {
		return node;
	}

	/**
	 * @param node the node to set
	 */
	public void setNode(Node node) {
		this.node = node;
	}

}
